import { pathOr } from 'ramda';
import urlPathConcat from '@kuiper-helpers/url-path-concat';

import pkg from '../../../package.json';

import { AVILIABLE_TASK_MAP, JOB_TASK_TYPE_MAP } from '../../common/constants';
import errorCodesBuilder from '../helpers/errorCodesBuilder';
// import workers from './workers';
// import alert from './alert';
import calcBalance from './updateBalance';
// import { intervalToMillis } from '../helpers/nextExecMillis';
import fetch from 'fermionjs/lib/fetch';
import warningCounts from '../helpers/warningCounts';
const { name } = pkg;

const errorCodes = errorCodesBuilder('任务执行失败');

export default function taskQueueInit(server, options) {}

export function scheduleNextTask({ server, queueTask }) {
	const {
		app: {
			model: { Asset, Job, Task },
		},
		methods: { addTask },
	} = server;
	const { triggerType, taskType, refId, recordId, taskIdx } = queueTask.data;
	if (triggerType !== 'schedule') return;

	setTimeout(async () => {
		let task = await Task.findOne({ _id: queueTask.id || queueTask.data.id });
		if (!task || task.status === 'stopped') {
			server.log.error(
				{ taskType, queueTask },
				'[QUEUE]添加下个周期的任务: 任务已停止'
			);
			return;
		}

		const ref = await (taskType === 'asset'
			? Asset.findOne({ _id: refId })
			: Job.findOne({ _id: refId }));

		if (!ref) {
			server.log.debug(
				{ taskType, recordId, taskIdx, refId },
				'[QUEUE]添加下个周期的任务 RefId 对应记录未找到'
			);
			throw new Error(`scheduleNextTask: Not found Ref ${refId}`);
		}

		const taskSettings =
			taskType === 'asset'
				? ref.taskSettings
				: pathOr(
						{},
						AVILIABLE_TASK_MAP[taskType]
							? [`${taskType}Settings`, taskIdx]
							: [`${taskType}Settings`],
						ref
				  );
		const enable = taskSettings.enable;

		// 未启用监测
		if (taskType === 'asset' ? !enable : !ref.enableMonitor || !enable) return;

		try {
			task = await addTask({
				taskType,
				taskIdx,
				taskSettings,
				refRecord: ref,
			});
		} catch (error) {
			server.log.error(
				{ error, taskType, taskIdx, taskSettings },
				'[QUEUE]添加下个周期的任务: 下发任务失败'
			);
			return;
		}

		if (taskType === 'asset') {
			await Asset.updateOne({ _id: refId }, { $set: { taskId: task._id } });
		} else if (AVILIABLE_TASK_MAP[taskType]) {
			await Job.updateOne(
				{ _id: refId },
				{
					$set: {
						[`${taskType}StatusId.${taskIdx}`]: task._id,
						[`${taskType}NextExecDate.${task.taskIdx}`]: task.nextExecDate,
					},
				}
			);
		} else {
			await Job.updateOne(
				{ _id: refId },
				{
					$set: {
						[`${taskType}StatusId`]: task._id,
						[`${taskType}NextExecDate`]: task.nextExecDate,
					},
				}
			);
		}

		server.log.debug(
			{ taskType, recordId, taskIdx, taskSettings },
			'[QUEUE]添加下个周期的任务'
		);
	}, 0);
}

export function handleTaskCompletedBuilder(server) {
	const handleTaskCompleted = async (queueTask, result) => {
		server.log.info(
			{ recordId: queueTask.data.recordId },
			'[QUEUE]任务执行完成'
		);

		const {
			data: { recordId, taskType, taskIdx },
		} = queueTask;
		const {
			app: {
				config: {
					[name]: { serviceApi, token },
				},
				model: { Task, Asset, Job, ResultNumbers },
			},
		} = server;

		let ref;
		let task;
		try {
			task = await Task.findOne({ _id: recordId });
			if (!task) {
				return server.log.error(
					{ recordId },
					'[QUEUE]任务执行完成更新Mongo记录，没有找到对应记录'
				);
			}
			task.result = result;
			task.status = 'completed';
			task.progress = 100;
			task.endTime = new Date();
			task.isLatestTask = true;
			const ret = await Promise.all([
				(taskType === 'asset' ? Asset : Job).findOne({
					_id: task.refId,
				}),
				Task.updateMany(
					{
						taskType: task.taskType,
						isLatestTask: true,
						_id: { $ne: task._id },
						refId: task.refId,
						isSpecialTask: task.isSpecialTask,
					},
					{ $set: { isLatestTask: false } }
				),
			]);
			ref = ret[0];
		} catch (err) {
			const error = errorCodes['[01]查询Mongo任务记录失败'].from(err);
			server.log.error(
				{ error, taskType, recordId, result, queueTask: queueTask.data },
				error.message
			);
			throw error;
		}

		if (!ref) {
			server.log.warn({ task }, '处理任务完成逻辑：未查询到关联的Ref记录');
			return task;
		}

		if (task.status === 'stopped') {
			server.log.warn({ queueTask }, '处理任务完成逻辑：任务已停止');
			return task;
		}

		let warnings = result?.warnings || [];
		task.securityStatus = warnings.length > 0 ? 'warning' : 'safe';
		if (task.refType === 'job' && result?.warnings?.length) {
			warnings = warnings.map(x => {
				if (ref.ignoredWarnIdMap[x.id]) {
					x.ignoredTime = ref.ignoredWarnIdMap[x.id];
				}
				return x;
			});
			task.result.warnings = warnings;
		}
		if (
			warnings.length > 0 &&
			// 目前不会有 isResolved 为 true 的
			warnings.every(x => x.isResolved || x.ignoredTime)
		) {
			task.securityStatus = 'safe';
		}
		Object.assign(task.result, warningCounts(warnings));

		// 更新任务配置记录securityStatus字段
		if (JOB_TASK_TYPE_MAP[taskType]) {
			if (AVILIABLE_TASK_MAP[taskType]) {
				ref[`${task.taskType}SecurityStatus`].splice(
					task.taskIdx,
					1,
					task.securityStatus
				);
			} else if (task.triggerType !== 'special') {
				ref[`${task.taskType}SecurityStatus`] = task.securityStatus;
			}
		} else {
			// Asset
			ref.securityStatus = task.securityStatus;
		}

		// 更新job/asset中isStarted字段
		if (JOB_TASK_TYPE_MAP[taskType]) {
			if (AVILIABLE_TASK_MAP[taskType]) {
				ref[`${task.taskType}IsStarted`].splice(task.taskIdx, 1, false);
			} else {
				ref[`${task.taskType}IsStarted`] = false;
			}
		} else {
			ref.isStarted = false;
		}

		let resultNumbers = warnings.reduce(
			(obj, item) => {
				if (item.level === 1) {
					obj.lowWarnCount++;
				} else if (item.level === 2) {
					obj.mediumWarnCount++;
				} else {
					obj.highWarnCount++;
				}
				return obj;
			},
			{
				highWarnCount: 0,
				mediumWarnCount: 0,
				lowWarnCount: 0,
			}
		);
		if (task.triggerType !== 'special') {
			if (taskType === 'asset') {
				const prevResultId = ref.resultId;
				ref.resultId = task.id;
				if (prevResultId) ref.prevResultId = prevResultId;
			} else if (AVILIABLE_TASK_MAP[taskType]) {
				const prevResultId = ref[`${taskType}ResultId`][taskIdx];
				ref[`${taskType}ResultId`].splice(taskIdx, 1, task.id);
				ref[`${taskType}Abnormal`].splice(
					taskIdx,
					1,
					task.securityStatus === 'warning'
				);

				resultNumbers = result?.addition?.detail?.reduce(
					(res, item) => {
						if (item.securityStatus === 'warning') {
							res.abnorNodeCount++;
						} else {
							res.normalNodeCount++;
						}
						res.totalNodeCount++;
						return res;
					},
					{
						...resultNumbers,
						abnorNodeCount: 0,
						normalNodeCount: 0,
						totalNodeCount: 0,
					}
				);
				try {
					await new ResultNumbers({
						taskType,
						uid: task.uid,
						jobId: ref._id,
						taskName: task.name,
						target: task.target,
						datetime: task.endTime,
						taskId: task._id,
						warnCount: warnings.length,
						aviliable: result?.addition?.detail,
						...resultNumbers,
					}).save();
				} catch (err) {
					const error = errorCodes['[01]新增可用性任务数据失败'].from(err);
					server.log.error(
						{ error, taskType, recordId, result, queueTask: queueTask.data },
						error.message
					);
					throw error;
				}

				if (prevResultId) {
					ref[`${taskType}PrevResultId`].splice(taskIdx, 1, prevResultId);
				}
			} else {
				const prevResultId = ref[`${taskType}ResultId`];
				ref[`${taskType}ResultId`] = task.id;
				ref[`${taskType}Abnormal`] = task.securityStatus === 'warning';

				try {
					await new ResultNumbers({
						taskType,
						uid: task.uid,
						jobId: ref._id,
						taskName: task.name,
						target: task.target,
						datetime: task.endTime,
						taskId: task._id,
						warnCount: warnings.length,
						...resultNumbers,
					}).save();
				} catch (err) {
					const error = errorCodes['[01]新增非可用性任务数据失败'].from(err);
					server.log.error(
						{ error, taskType, recordId, result, queueTask: queueTask.data },
						error.message
					);
					throw error;
				}

				if (prevResultId) ref[`${taskType}PrevResultId`] = prevResultId;
			}
		}

		try {
			const job = await Job.findOne({ _id: task.jobId });
			if (!job) {
				task.remove();
				throw new Error(`任务执行完成没有找到关联的job：${task.jobId}`);
			}
			const taskAlertEnable = task.taskSettings.alert?.enable;
			task.alertSettings = {
				...job.alertSettings,
				enable:
					task.triggerType === 'schedule' ||
					task.triggerType === 'scheduleIncrement'
						? job.enableAlert && job.alertSettings.enable[taskType]
						: taskAlertEnable === undefined
						? true
						: taskAlertEnable,
			};

			await Promise.all([task.save(), ref.save()]);
		} catch (err) {
			const error = errorCodes['[01]更新结果失败'].from(err);
			server.log.error({ error, taskType, recordId }, error.internalMessage);
		}

		setImmediate(async () => {
			try {
				const sendAlertTypes = await alert({
					task,
					ref,
					server,
					taskIdx,
				});
				calcBalance({ task, server, sendAlertTypes });
				const yunaqBlacklistUrl = urlPathConcat(
					serviceApi,
					'/api/v2/cloud/udb_system_add_blacklist'
				);
				fetch(yunaqBlacklistUrl, {
					method: 'POST',
					headers: {
						cookie: `token=${token}`,
					},
					body: JSON.stringify({
						taskIds: [task._id],
					}),
				}).then(resp =>
					server.log.info(
						{ resp, api: yunaqBlacklistUrl, taskId: task._id },
						'加黑请求发送成功'
					)
				);
			} catch (err) {
				const error = errorCodes['[0A][发送告警]告警信息发送失败'].from(err);
				server.log.error({ error, taskType, recordId }, error.message);
			}
		});
	};

	return (queueTask, result) => handleTaskCompleted(queueTask, result);
}

export function handleTaskFailed(server) {
	return async (queueTask, err) => {
		const error = errorCodes['[01]任务队列任务执行失败'].from(err);
		server.log.error(
			{ error, queueTask, queueTaskId: queueTask.id },
			error.internalMessage
		);
		try {
			await updateRefIsStarted({ server, queueTask });
			await updateTaskStatus({
				server,
				queueTaskId: queueTask.id,
				status: 'failed',
				endTime: new Date(),
				exception: err.message,
			});
		} catch (error) {}
	};
}

// 更新ref的isStarted(是否在执行中)字段
async function updateRefIsStarted({ queueTask, server, isStarted = false }) {
	const {
		app: {
			model: { Task, Job, Asset },
		},
	} = server;

	const { taskType, recordId: taskId, taskIdx } = queueTask.data;
	let task;

	try {
		task = await Task.findById(taskId);
	} catch (err) {
		const error = errorCodes['[01][更新ref的子任务执行状态]查找任务失败'].from(
			err
		);
		server.log.error({ error, taskId }, error.rawMessage);
	}
	let refModel;
	if (task && task.refId) {
		if (JOB_TASK_TYPE_MAP[taskType]) {
			refModel = Job;
			if (AVILIABLE_TASK_MAP[taskType]) {
				try {
					await refModel.updateOne(
						{ _id: task.refId },
						{
							$set: {
								[`${task.taskType}IsStarted.${taskIdx}`]: isStarted,
							},
						}
					);
				} catch (err) {
					const error = errorCodes[
						'[01][更新ref的子任务执行状态]ref更新状态失败'
					].from(err);
					server.log.error(
						{ error, taskType: task.taskType, refId: task.refId },
						error.rawMessage
					);
				}
			} else {
				try {
					await refModel.updateOne(
						{ _id: task.refId },
						{ $set: { [`${task.taskType}IsStarted`]: isStarted } }
					);
				} catch (err) {
					const error = errorCodes[
						'[01][更新ref的子任务执行状态]ref更新状态失败'
					].from(err);
					server.log.error(
						{ error, taskType: task.taskType, refId: task.refId },
						error.rawMessage
					);
				}
			}
		} else {
			try {
				await Asset.updateOne(
					{ _id: task.refId },
					{ $set: { isStarted: isStarted } }
				);
			} catch (err) {
				const error = errorCodes[
					'[01][更新ref的子任务执行状态]ref更新状态失败'
				].from(err);
				server.log.error(
					{ error, taskType: task.taskType, refId: task.refId },
					error.rawMessage
				);
			}
		}
	}
}

async function updateTaskStatus({
	queueTaskId,
	server,
	queueTask,
	status,
	endTime,
	exception,
	progress,
}) {
	const {
		app: {
			model: { Task },
		},
	} = server;

	return Task.updateOne(
		{ _id: queueTaskId, status: { $ne: 'stopped' } },
		{
			$set: {
				...(status ? { status } : {}),
				...(endTime ? { endTime } : {}),
				...(exception ? { exception } : {}),
				...(progress ? { progress } : {}),
			},
		}
	).catch(err => {
		const error = errorCodes['[01]更新任务状态失败'].from(err);
		server.log.error({ error, queueTask }, error.rawMessage);
	});
}
